<?php

class IValidator extends IObject {
	private $errors = array();
	private $name;
	private $value;
	private $real;
	
	public function __construct($name, $value, $opts = array()) {
		$this->name = $name;
		$this->real = $value;
		$this->value = $value;
		
		if (!isset($opts["trim"]) || $opts["trim"]) {
			$this->value = trim($this->value);
		}
	}
	
	public function apply($rule, $params = null, $message = null) {
		$callback = null;
		if (is_array($rule) && isset($rule[0]) && isset($rule[1])) {
			$callback = array($rule[0], $rule[1]);
		}
		else if (is_string($rule)) {
			$callback = array($this, $rule);;
		}
		if (!method_exists($callback[0], $callback[1])) {
			throw new IException("undefined validate rule '" . $callback[1] .  "'");
		}
		else {
			if (is_null($message)) {
				$message = "failed to check '" . $callback[1] . "' for {$this->name}";
			}
			$bool = call_user_func_array($callback, array($params, $message));
			if (!$bool) {
				$this->errors[] = $message;
			}
		}
	}
	
	public function applyOpts(array $opts) {
		if (!empty($opts)) {
			$rule = $opts[0];
			$params = isset($opts[1])?$opts[1]:null;
			$message = isset($opts[2])?$opts[2]:null;;
			$this->apply($rule, $params, $message);
		}
	}
	
	public function errors() {
		return $this->errors;
	}
	
	public function length($params = null) {
		if (is_array($params)) {
			$length = strlen($this->value);
			if (isset($params["min"]) && $params["min"] > $length) {
				return false;
			}
			if (isset($params["max"]) && $params["max"] < $length) {
				return false;
			}
		}
		return true;
	}
	
	public function required($params = null) {
		if ($this->value == "") {
			return false;
		}
		return true;
	}
	
	public function notNull($params = null) {
		if (is_null($this->real)) {
			return false;
		}
		return true;
	}
	
	public function notEmpty($params = null) {
		if (empty($this->value)) {
			return false;
		}
		return true;
	}

	public function alpha($params = null) {
		if (!preg_match("/^[a-z]+$/i", $this->value)) {
			return false;
		}
		return true;
	}
	
	public function word($params = null) {
		if (!preg_match("/^\w+$/", $this->value)) {
			return false;
		}
		return true;
	}
	
	public function alphaNum($params = null) {
		if (!preg_match("/^[\da-z]+$/i", $this->value)) {
			return false;
		}
		return true;
	}
	
	public function between($params = null) {
		if (is_array($params)) {
			if (isset($params["min"]) && $this->value < $params["min"]) {
				return false;
			}
			if (isset($params["max"]) && $this->value > $params["max"]) {
				return false;
			}
		}
		return true;
	}
	
	public function blank($params = null) {
		if (preg_match("/[^\s]/", $this->value)) {
			return false;
		}
		return true;
	}
	
	public function greater($params = null) {
		if (is_scalar($params) && $this->value <= $params) {
			return false;
		}
		return true;
	}
	
	public function less($params = null) {
		if (is_scalar($params) && $this->value >= $params) {
			return false;
		}
		return true;
	}
	
	public function greaterEqual($params = null) {
		if (is_scalar($params) && $this->value < $params) {
			return false;
		}
		return true;
	}
	
	public function lessEqual($params = null) {
		if (is_scalar($params) && $this->value > $params) {
			return false;
		}
		return true;
	}
	
	public function equal($params = null) {
		if (is_scalar($params) && $this->value != $params) {
			return false;
		}
		return true;
	}
	
	public function notEqual($params = null) {
		if (is_scalar($params) && $this->value == $params) {
			return false;
		}
		return true;
	}
	
	public function regex($params = null) {
		if (!is_array($params)) {
			$params = array($params);
		}
		foreach ($params as $param) {
			if (is_string($param) && !@preg_match($param, $this->value)) {
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Enter description here...
	 * 
	 * 此处参考了CakePHP的实现 http://api.cakephp.org/validation_8php-source.html
	 *
	 * @param unknown_type $params
	 */
	public function date($params = null) {
		$regex = array();
		$regex["dmy"] = "%^(?:(?:31(\\/|-|\\.|\\x20)(?:0?[13578]|1[02]))\\1|(?:(?:29|30)(\\/|-|\\.|\\x20)(?:0?[1,3-9]|1[0-2])\\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$|^(?:29(\\/|-|\\.|\\x20)0?2\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\\d|2[0-8])(\\/|-|\\.|\\x20)(?:(?:0?[1-9])|(?:1[0-2]))\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$%";
         $regex["mdy"] = "%^(?:(?:(?:0?[13578]|1[02])(\\/|-|\\.|\\x20)31)\\1|(?:(?:0?[13-9]|1[0-2])(\\/|-|\\.|\\x20)(?:29|30)\\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$|^(?:0?2(\\/|-|\\.|\\x20)29\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:(?:0?[1-9])|(?:1[0-2]))(\\/|-|\\.|\\x20)(?:0?[1-9]|1\\d|2[0-8])\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$%";
         $regex["ymd"] = "%^(?:(?:(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(\\/|-|\\.|\\x20)(?:0?2\\1(?:29)))|(?:(?:(?:1[6-9]|[2-9]\\d)?\\d{2})(\\/|-|\\.|\\x20)(?:(?:(?:0?[13578]|1[02])\\2(?:31))|(?:(?:0?[1,3-9]|1[0-2])\\2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\\2(?:0?[1-9]|1\\d|2[0-8]))))$%";
         $regex["dMy"] = "/^((31(?!\\ (Feb(ruary)?|Apr(il)?|June?|(Sep(?=\\b|t)t?|Nov)(ember)?)))|((30|29)(?!\\ Feb(ruary)?))|(29(?=\\ Feb(ruary)?\\ (((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)))))|(0?[1-9])|1\\d|2[0-8])\\ (Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)\\ ((1[6-9]|[2-9]\\d)\\d{2})$/";
         $regex["Mdy"] = "/^(?:(((Jan(uary)?|Ma(r(ch)?|y)|Jul(y)?|Aug(ust)?|Oct(ober)?|Dec(ember)?)\\ 31)|((Jan(uary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sept|Nov|Dec)(ember)?)\\ (0?[1-9]|([12]\\d)|30))|(Feb(ruary)?\\ (0?[1-9]|1\\d|2[0-8]|(29(?=,?\\ ((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)))))))\\,?\\ ((1[6-9]|[2-9]\\d)\\d{2}))$/";
         $regex["My"] = "%^(Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)[ /]((1[6-9]|[2-9]\\d)\\d{2})$%";
         $regex["my"] = "%^(((0[1-9]|10|11|12)([- /.])(([1][9][0-9][0-9])|([2][0-9][0-9][0-9]))))$%";
         if (isset($regex[$params]) && !preg_match($regex[$params], $this->value)) { 
			return false;		
         }
         return true;
	}
	
	/**
	 * Enter description here...
	 * 
	 * (HH:MM) or am/pm ([H]H:MM[a|p]m)
	 *
	 * @param unknown_type $params
	 * @return unknown
	 */
	public function time($params = null) {
		$regex = "%^((0?[1-9]|1[012])(:[0-5]\d){0,2}([AP]M|[ap]m))$|^([01]\d|2[0-3])(:[0-5]\d){0,2}$%";
		if (!preg_match($regex, $this->value)) {
			return false;
		}
		return true;
	}
	
	public function bool($params = null) {
		$values = array();
		if (is_array($params) && isset($params["strict"]) && $params["strict"]) {
			$values = array(true, false);
			if (!in_array($this->real, $values, true)) {
				return false;
			}
		}
		else {
			$values = array(0, 1, "0", "1", true, false);
			if (!in_array($this->value, $values, true)) {
				return false;
			}
		}
		return true;
	}
	
	public function email($params = null) {
		$regex = "/^[a-z0-9]+([\._\-\+]*[a-z0-9]+)*@([a-z0-9]+[\-a-z0-9]*[a-z0-9]+\.)+[a-z0-9]+$/i";
		if (!preg_match($regex, $this->value)) {
			return false;
		}
		return true;
	}
	
	public function ext($params = null) {
		$ext = pathinfo($this->value, PATHINFO_EXTENSION);
		if (is_string($params)) {
			$params = explode(",", $params);
		}
		if (is_array($params) && !in_array($ext, $params) && !in_array("." . $ext, $params)) {
			return false;
		}
		return true;
	}
	
	public function ip($params = null) {
		$long = @ip2long($this->value);
		if ($long == -1 || $long === false) {
			return false;
		}
		return true;
	}
	
	public function minLength($params = null) {
		return $this->length(array("min" => $params));
	}
	
	public function maxLength($params = null) {
		return $this->length(array("max" => $params));
	}
	
	public function numeric($params = null) {
		if (!is_numeric($this->value)) {
			return false;
		}
		return true;
	}
	
	public function range($params = null) {
		if (is_array($params)) {
			$low = isset($params["low"])?$params["low"]:0;
			$high = isset($params["high"])?$params["high"]:0;
			$step = isset($params["step"])?$params["step"]:1;
			$strict = isset($params["strict"])?$params["strict"]:false;
			$range = range($low, $high, $step);
			if (!in_array($this->value, $range, $strict)) {
				return false;
			}
		}
		return true;
	}
	
	public function in($params = null) {
		if (is_array($params) && !in_array($this->value, $params)) {
			return false;
		}
		return true;
	}
}


?>